package org.Shiikong_Memory.SakuraBlockFrame.PluginCommunication;

import lombok.Getter;
import lombok.Setter;
import org.Shiikong_Memory.SakuraBlockFrame.DataTool.RedisTool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 插件通信服务类，提供插件之间基于 Redis 的消息通信功能，
 * 支持广播消息、定向消息发送，以及订阅消息和上线通知等功能。
 * @author Shiikong_Memory
 * @version 1.0
 */
public class PluginCommunicationService {
    /**
     * Redis 工具类实例，用于与 Redis 进行交互。
     */
    private final RedisTool redisTool;
    /**
     * 广播频道名称，初始值可被构造函数修改。
     * 注：初始值设定存在冗余，可考虑优化。
     */
    @Setter
    private String BROADCAST_CHANNEL;
    /**
     * 上线通知频道名称，初始值可被构造函数修改。
     * 注：初始值设定存在冗余，可考虑优化。
     */
    @Setter
    private String ONLINE_CHANNEL;
    /**
     * 存储订阅频道及其对应的消息处理器列表的并发映射。
     */
    private final ConcurrentMap<String, List<MessageHandler>> subscriptionHandlers = new ConcurrentHashMap<>();
    // 获取自身 ID 的方法
    /**
     * 存储自身的唯一标识。
     */
    @Getter
    @Setter
    private String selfId; // 新增：存储自身 ID

    private final ConcurrentMap<String, JedisPubSub> activeSubscriptions = new ConcurrentHashMap<>();
    private volatile boolean isRunning = true;

    /**
     * 构造函数，初始化插件通信服务。
     *
     * @param pluginName 插件名称，用于生成广播频道和上线通知频道的名称
     * @param redisTool  Redis 工具类实例，用于与 Redis 进行交互
     */
    public PluginCommunicationService(String pluginName, RedisTool redisTool) {
        this.redisTool = redisTool;
        this.selfId = UUID.randomUUID().toString(); // 使用 UUID 生成唯一 ID
        BROADCAST_CHANNEL = pluginName + "_broadcast";
        ONLINE_CHANNEL = pluginName + "_online";
    }

    /**
     * 广播消息的方法，将消息发送到广播频道。
     *
     * @param messageType    消息类型
     * @param messageContent 消息内容
     */
    public void broadcastMessage(String messageType, String messageContent) {
        MessagePayload payload = new MessagePayload(selfId, null, messageType, messageContent);
        try (Jedis jedis = redisTool.getJedisPool().getResource()) {
            jedis.publish(BROADCAST_CHANNEL, payload.toJson());
        }
    }

    /**
     * 定向发送消息的方法，将消息发送指定的插件。
     *
     * @param receiverId        对象唯一标识
     * @param messageType       消息类型
     * @param messageContent    消息内容
     */
    public void sendDirectMessage(String receiverId, String messageType, String messageContent) {
        MessagePayload payload = new MessagePayload(selfId, receiverId, messageType, messageContent);
        try (Jedis jedis = redisTool.getJedisPool().getResource()) {
            jedis.publish(receiverId, payload.toJson());
        }
    }

    /**
     * 订阅与自身有关的消息，用于获取处理定向发送给自己的消息
     * @param handler 通讯消息监听器
     */
    public void subscribeSelfMessage(MessageHandler handler) {
        subscriptionHandlers.computeIfAbsent(selfId, k -> new ArrayList<>()).add(handler);
        executeSubscription(selfId);
    }

    /**
     * 订阅广播消息
     * @param handler 通讯消息监听器
     */
    public void subscribeBroadcastMessage(MessageHandler handler) {
        subscriptionHandlers.computeIfAbsent(BROADCAST_CHANNEL, k -> new ArrayList<>()).add(handler);
        executeSubscription(BROADCAST_CHANNEL);
    }

    /**
     * 消息监听器注册通用方法
     * @param channel 频道ID
     */
    private void executeSubscription(String channel) {
        new Thread(() -> {
            try (Jedis newJedis = redisTool.getJedisPool().getResource()) {
                JedisPubSub pubSub = new JedisPubSub() {
                    @Override
                    public void onMessage(String channel1, String message) {
                        if (!isRunning) return;
                        MessagePayload payload = MessagePayload.fromJson(message);
                        List<MessageHandler> handlers = subscriptionHandlers.get(channel1);
                        if (handlers != null) {
                            for (MessageHandler h : handlers) {
                                h.handle(channel1, payload);
                            }
                        }
                    }
                };
                activeSubscriptions.put(channel, pubSub);
                newJedis.subscribe(pubSub, channel);
            } catch (Exception e) {
                if (isRunning) {
                    throw new RuntimeException("订阅失败", e);
                }
            }
        }).start();
    }

    /**
     * 广播发送上线通知，用于告诉其他插件自身ID的方法
     */
    public void sendOnlineNotification() {
        MessagePayload payload = new MessagePayload(selfId, null, "ONLINE", "Plugin is online");
        try (Jedis jedis = redisTool.getJedisPool().getResource()) {
            jedis.publish(ONLINE_CHANNEL, payload.toJson());
        }
    }

    // 辅助方法，用于订阅消息
    private void subscribeMessage(String channel, MessageHandler handler) {
        subscriptionHandlers.computeIfAbsent(channel, k -> new ArrayList<>()).add(handler);
        executeSubscription(channel);
    }

    /**
     * 订阅上线通讯，用于获取正在上线设备的ID
     * @param handler 通讯消息监听器
     */
    public void subscribeOnlineNotification(MessageHandler handler) {
        subscribeMessage(ONLINE_CHANNEL, handler);
    }

    public void stop() {
        isRunning = false;
        activeSubscriptions.forEach((channel, pubSub) -> {
            pubSub.unsubscribe();
        });
        activeSubscriptions.clear();
        subscriptionHandlers.clear();
    }
}